# 区分进程和线程

  1. 进程:是cpu资源分配的最小单位(是能够拥有资源和独立运行的最小单位,每个进程有一个或多个线程)
  2. 线程:是调度cpu资源的最小单位(是建立在进程基础之上的一次程序运行单位,常说的“单线程或多线程”都是指的在同一个进程中)

# 浏览器是多进程的

以下均以 Chrome 为例,在某些细节上,与 Firefox、Safari 不同,例如 Chrome 由渲染进程完成最终的绘制,而后两者则由主进程完成。

怎样理解:

  1. 浏览器是一个多进程应用
  2. 浏览器之所以能够运行,是因为系统为它分配了资源(cpu、内存)
  3. 没打开一个tab页,就会创建一个独立的进程

主要进程:

  1. 主进程(Browser进程),只有一个,负责协调主控:

    • 负责头部区域的交互:前进、后退、刷新、收藏等;
    • 负责tab页的管理:创建、销毁等;
    • 网络资源的管理和下载。
  2. 插件进程,每个浏览器插件对应一个进程,仅在使用时才创建。

  3. GPU 进程,最多一个,用于3D绘制等

  4. 渲染进程(浏览器内核),默认每个Tab页都是一个单独的进程,但当开多个空tab时,chrome会将其优化合并成一个线程

多进程优势:

  1. 利用沙盒模型隔离不同进程,当一个进程崩溃后,不会影响整个浏览器运行,提高了稳定性;
  2. 充分利用操作系统多核优势

多进程劣势:

  1. 资源(cpu、内存)消耗较大

# 渲染进程

主要线程:

  1. GUI 渲染线程
    • 负责解析HTML、CSS,构建DOM树、CSSOM树,生成渲染树,布局、绘制等
    • 重排、重绘
    • 与 JS 线程互斥,JS 线程运行时,渲染线程将会挂起
  2. JS 引擎线程
    • 即 JS 内核,如 V8 引擎,负责处理 JS 脚本程序
    • 与渲染线程互斥,长时间执行会导致渲染卡顿,阻塞渲染
  3. 事件触发线程
    • 控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
    • 当JS引擎执行代码块如setTimeout、点击事件、ajax等,会将对应的回调事件注册到事件触发线程中
    • 当对应的回调事件符合触发条件被触发时,该线程会把事件添加到事件队列的队尾,等待JS引擎的处理
    • 当JS引擎空闲时会从事件队列中取出响应事件进行执行
  4. 定时器触发线程
    • setInterval与setTimeout所在线程
    • 浏览器定时计数并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
    • 因此通过单独线程来计时并触发定时(开始计时时,将回调事件注册到事件触发线程中,计时结束后,由事件触发线程添加到事件队列中,等待JS引擎空闲后执行)
    • W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
  5. 异步http请求线程
    • 负责处理异步请求
    • 异步请求建立后,将对应回调注册到事件触发线程中,当异步状态变更时,由事件触发线程将对应回调事件(成功或失败)添加到事件队列中,等待JS引擎空闲后执行

渲染进程中主要的线程

# 进程间协作

有疑问:不同进程的职责

  • 主进程创建渲染进程;
  • 输入 url 后,主进程接管,并创建一个下载线程请求该 url 内容;
  • 获取内容后通过 RendererHost 接口转交给渲染进程;
  • 渲染进程渲染页面

# 渲染进程中线程间协作

# GUI 渲染线程与 JS 引擎线程互斥

  • 由于 JS 可以操作 DOM ,做在渲染的同时修改 DOM 将造成渲染结果的不确定性,所以必须互斥。
  • 同理,如果是多线程,每个线程都去修改 DOM,也会造成不确定性,所以必须是单线程。
  • JS 引擎线程执行时间过程,将导致阻塞渲染或者交互卡顿。
  • GUI 渲染线程会在 JS 引擎线程空闲时再执行。

# 解决阻塞渲染或交互卡顿问题

  1. WebWorker

    • 创建 Worker 时,JS 引擎线程向渲染进程申请开一个 Worker 线程,该线程不能操作 dom;
    • JS 引擎线程与 Worker 线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据);
    • 适用于处理耗时的 cpu 密集型大量计算任务,待任务执行结束后,将结果通知给 JS 引擎线程;
    • JS 引擎的单线程本质没有改变。
  2. 区域与SharedWorker

    • WebWorker 是渲染进程中的一个共享线程,而 SharedWorker 是所有同源页面(渲染线程)共享的一个进程;
    • 可以实现同源 Tab 页面间的通信。

# 渲染流程

浏览器渲染流程

资源加载后触发相应事件:

  • DOMContentLoaded:当 DOM 内容加载和解析完成时触发;
  • onload:当所有 DOM、css、js、图片等都加载完成时触发;
  • 注意是加载完成不是渲染完成
  • 执行顺序:DOMContentLoaded 早于 onload

# css 文件加载的阻塞问题

  • css 由异步http请求线程异步加载,不影响其它文件加载
  • 不会阻塞 DOM 解析:异步加载时不影响 DOM 树解析和构建
  • 会阻塞页面渲染:render 树需要 css 信息,所以会阻塞生成 render 树,从而阻塞页面渲染。如果 css 加载不影响生成 render 树,若未加载的 css 样式会影响某个 dom,当加载完成后,还要重新生成 render 树,甚至导致重排或重绘,影响性能。所以,css 加载会阻塞生成 render 树。

# 待解决疑问

  1. 页面的渲染(最终绘制)是由“主进程”或“GPU进程”还是“渲染进程的 GUI 线程”完成?

  2. 页面中遇到静态资源的加载(css、js),是由“主进程完成”还是“渲染进程的异步 HTTP 线程”完成?

  3. 异步请求应该是由“渲染进程的异步 HTTP 线程”完成的吧?

参考: https://segmentfault.com/a/1190000012925872

最后更新时间: 6/3/2021, 8:02:18 PM